﻿// Copyright 2010 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions at http://help.arcgis.com/en/sdk/10.0/usageRestrictions.htm
// 
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using ESRI.ArcGIS.ADF.Web;
using ESRI.ArcGIS.ADF.Web.UI.WebControls;
using ESRI.ArcGIS.ADF.Web.DataSources;
using System.Collections.Specialized;
using ESRI.ArcGIS.ADF.Web.Geometry;
using ESRI.ArcGIS.ADF.Web.Display.Graphics;
namespace ESRI.ADF.UserSearch.CustomTasks
{
    // Default markup inserted when the task is dragged from the toolbox onto a page in Visual Studio
    [System.Web.UI.ToolboxData(@"<{0}:QueryBuilderTask runat=""server"" BackColor=""White""
        BorderColor=""LightSteelBlue"" BorderStyle=""Outset"" BorderWidth=""1px"" Font-Names=""Verdana""
        Font-Size=""8pt"" Font-color=""White"" ForeColor=""Black"" TitleBarColor=""#2F5675"" TitleBarHeight=""20px""
        TitleBarSeparatorLine=""True"" Transparency=""35"" Width=""130px"" Title=""Tìm kiếm""
        HeightResizable=""False"" WidthResizable=""False"">
        </{0}:QueryBuilderTask>")]
    // Image used to represent the task in a container (e.g. the Visual Studio toolbox)
    [System.Drawing.ToolboxBitmap(typeof(QueryBuilderTask))]
    // Class used to configure the task in ArcGIS Manager
    //[ESRI.ArcGIS.ADF.Web.UI.WebControls.WebConfigurator(typeof(QueryBuilderTaskWebConfigurator_CSharp))]
    // Class used to configure the task in Visual Studio
    //[System.ComponentModel.Designer(typeof(QueryBuilderTaskDesigner_CSharp))]
    // QueryBuilderTask's implementation
    [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientTaskDefinitionExplicit]
    public class QueryBuilderTask : ESRI.ADF.UserSearch.CustomTasks.ScriptTask
    {
        #region Instance Variables - Controls Exposed to Client-Side Task

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlGenericControl _layerDropDownList;
        
        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        System.Web.UI.HtmlControls.HtmlGenericControl _retrievingLayersDiv;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlGenericControl _fieldDropDownList;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        System.Web.UI.HtmlControls.HtmlGenericControl _retrievingFieldsDiv;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlGenericControl _operatorDropDownList;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlGenericControl _sampleValueDropDownList;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        System.Web.UI.HtmlControls.HtmlGenericControl _retrievingSampleValuesDiv;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlInputButton _addToQueryButton;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlInputButton _andButton;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlInputButton _orButton;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlInputButton _notButton;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlInputButton _leftParenthesisButton;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlInputButton _rightParenthesisButton;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlInputButton _percentButton;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlInputButton _underscoreButton;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.WebControls.TextBox _queryTextBox;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlInputButton _clearQueryButton;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        private System.Web.UI.HtmlControls.HtmlInputButton _doQueryButton;

        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        System.Web.UI.HtmlControls.HtmlGenericControl _executingQueryDiv;

        #endregion

        #region ASP.NET WebControl Life Cycle Event Overrides - CreateChildControls

        // Configures the task's interface
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            // Call methods to instantiate task controls and place them in html tables for formatting
            
            // Layers drop-down list
            this.CreateDropDownListSection(ref _layerDropDownList, ref _retrievingLayersDiv, "Lớp:", 
                "Lấy các lớp...");
            // Fields drop-down list
            this.CreateDropDownListSection(ref _fieldDropDownList, ref _retrievingFieldsDiv, "Trường:", 
                "Lấy các trường...");
            // Operators drop-down list
            this.CreateDropDownListSection(ref _operatorDropDownList, "Toán tử:");
            // Sample values drop-down list
            this.CreateDropDownListSection(ref _sampleValueDropDownList, ref _retrievingSampleValuesDiv, 
                "Các giá trị:", "Lấy các giá trị...");
            
            this.CreateAddToQueryButtonSection();
            this.CreateSeparator();
            this.CreateOperatorButtonsSection();
            this.CreateSeparator();
            this.CreateQuerySection();
            this.CreateQueryButtonsSection();
        }

        #endregion

        #region Instance Methods

        #region Public Methods Exposed to Client-Side Task

        // Retrieves the layers for the current map and packages the properties of each that are
        // required by the client-side task in a JSON array
        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        public string GetJsonLayers()
        {
            // Add the opening bracket of the JSON array
            string jsonLayerArray = "[ ";

            // Iterate through the resources in the buddied map, extracting the layer information
            // from each and adding it to the JSON array
            foreach (ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem mapResourceItem in
                this.MapControl.MapResourceManagerInstance.ResourceItems)
            {
                // If the current resource is a graphics resource and the ExcludeGraphicsResources
                // property is true, skip to the next resource item
                if (this.ExcludeGraphicsResources && mapResourceItem.Resource is
                ESRI.ArcGIS.ADF.Web.DataSources.Graphics.MapResource)
                    continue;

                // If the current resource does not support querying, skip to the next resource item
                if (!mapResourceItem.Resource.SupportsFunctionality(
                typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)))
                    continue;

                // Get the resource's map functionality
                ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality commonMapFunctionality =
                    this.MapControl.GetFunctionality(mapResourceItem);

                // Get the map's layers
                string[] layerIDsArray;
                string[] layerNamesArray;
                commonMapFunctionality.GetLayers(out layerIDsArray, out layerNamesArray);

                // Loop through the layers.  For each, add a JSON object with the properties needed 
                // by the client-side task
                for (int i = 1; i < layerIDsArray.Length; i++)
                {
                    jsonLayerArray += string.Format("{{ \"name\":\"{0}\", \"id\":\"{1}\", " +
                        "\"mapResource\":\"{2}\", \"map\":\"{3}\" }}, ", layerNamesArray[i], 
                        layerIDsArray[i], mapResourceItem.Name, this.Map);
                }
            }

            // Remove trailing comma and add a closing square bracket
            if (jsonLayerArray.Length > 2)
                jsonLayerArray = string.Format("{0} ]", jsonLayerArray.Substring(0, jsonLayerArray.Length - 2));
            
            return jsonLayerArray;
        }

        // Retrieves the fields for the layer specified by the passed-in JSON and returns the
        // properties of those fields in a JSON array
        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        public string GetJsonFields(string jsonLayer)
        {
            // Convert the passed-in JSON to a Dictionary object
            System.Web.Script.Serialization.JavaScriptSerializer javaScriptSerializer = 
                new System.Web.Script.Serialization.JavaScriptSerializer();
            System.Collections.Generic.Dictionary<string, object> layer = 
                javaScriptSerializer.DeserializeObject(jsonLayer) as
                System.Collections.Generic.Dictionary<string, object>;

            // Get the LayerFormat for the passed-in layer
            string mapResourceName = layer["mapResource"] as string;
            string layerID = layer["id"] as string;
            ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat layerFormat =
                ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat.FromMapResourceManager(
                this.MapControl.MapResourceManagerInstance, mapResourceName, layerID);

            // Add the opening bracket of the json Array
            string jsonFieldArray = "[ ";

            // Loop through all the fields, converting each visible one to JSON and adding it to
            // the array string
            foreach (ESRI.ArcGIS.ADF.Web.DataSources.FieldInfo fieldInfo in layerFormat.Fields)
            {
                if (fieldInfo.Visible)
                    jsonFieldArray += string.Format("{0},", fieldInfo.ToJson());
            }

            // Remove trailing comma and add closing square bracket
            if (jsonFieldArray.Length > 2)
                jsonFieldArray = string.Format("{0} ]", jsonFieldArray.Substring(0, jsonFieldArray.Length - 1));
            return jsonFieldArray;
        }

        // Retrieves values for the field, layer, and map resource specified.  Values are returned
        // as a JSON array.
        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        public string GetSampleValues(string fieldName, string layerID, string mapResourceName)
        {
            // Execute a query with an empty where clause on the passed-in layer.  This will return
            // all the features, subject to the service maximum.
            System.Data.DataTable valuesDataTable = 
                this.ExecuteQuery(null, fieldName, layerID, mapResourceName);

            // Replace periods in field name if necessary
            if (fieldName != valuesDataTable.Columns[0].ColumnName)
                fieldName = fieldName.Replace(".", "_");

            // Add the opening bracket of the JSON array that will store the values
            string jsonValueArray = "[ ";

            // Loop through the rows of the data table, adding the value of each for the specified
            // field to the JSON array
            for (int i = 0; i < valuesDataTable.Rows.Count; i++)
            {
                // Get field value from current row
                string value = valuesDataTable.Rows[i][fieldName].ToString();

                // Escape single and double quotes, as these will otherwise short-circuit calls to eval 
                // on the result that is returned to the client
                value = value.Replace("'", "\\'");
                value = value.Replace("\"", "\\\\\"");

                // Surround the value with quotes and add a trailing comma to format it as an item in a 
                // JSON array
                value = string.Format("\"{0}\",", value);

                // If the value is not already in the array and is not empty, add it to the array
                if ((jsonValueArray.IndexOf(value) < 0) && (value != "")
                && (value != " ") && (value != "NaN") && (value != null))
                    jsonValueArray += value;
            }

            // Remove trailing comma and add closing square bracket
            if (jsonValueArray.Length > 2)
                jsonValueArray = string.Format("{0} ]", jsonValueArray.Substring(0, jsonValueArray.Length - 1));

            return jsonValueArray;
        }

        // Executes a query as specified by the passed-in arguments and adds the results to the
        // task's first buddied TaskResults container
        [ESRI.ADF.UserSearch.CustomTasks.ScriptTask.ClientMember]
        public void DoQuery(string queryExpression, string layerID, string mapResourceName)
        {
            // Execute the query with the specified parameters
            System.Data.DataTable resultsDataTable = this.ExecuteQuery(queryExpression, null,
                layerID, mapResourceName);

            // Convert the results data table to a graphics layer
            ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer resultsGraphicsLayer =
                ESRI.ArcGIS.ADF.Web.Converter.ToGraphicsLayer(resultsDataTable);

            // Select each result so that its node is checked in the TaskResults control
            foreach (System.Data.DataRow dataRow in resultsGraphicsLayer.Rows)
                dataRow[resultsGraphicsLayer.IsSelectedColumn] = true;

            // Retrieve the query layer's layerFormat and apply it to the results layer
            ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat layerFormat =
                ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat.FromMapResourceManager(
                this.MapControl.MapResourceManagerInstance, mapResourceName, layerID);
            layerFormat.Apply(resultsGraphicsLayer);

            // Render the results on the client to enable callouts and highlighting
            resultsGraphicsLayer.RenderOnClient = true;

            // Create a DataSet and add the results to it
            string resultsDataSetName = string.Format("Kết quả truy vấn - {0}",
                resultsGraphicsLayer.TableName);
            System.Data.DataSet resultsDataSet = new System.Data.DataSet(resultsDataSetName);
            resultsDataSet.Tables.Add(resultsGraphicsLayer);

            // Get the task's first buddied TaskResults control esri_samples.gridResults
            ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults taskResultsContainer =
                ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControl(this.TaskResultsContainers[0].Name,
                this.Page) as ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults;
            esri_samples.gridResults taskResultsContainer1 =
                ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControl(this.TaskResultsContainers[0].Name,
                this.Page) as esri_samples.gridResults;

            ESRI.ArcGIS.ADF.Web.UI.WebControls.ITaskResultsContainer tskRC = taskResultsContainer1; 
            // Make sure a TaskResults control was found
            if (taskResultsContainer1 == null)
                return;

            // Display the query results.  Note the TaskResults container automatically handles
            // adding results to its buddied Map.
            tskRC.DisplayResults(null, null, null, resultsDataSet);
            //taskResultsContainer.DisplayResults(null, null, null, resultsDataSet);

            // Copy the task results callback results to the task so they are processed on the client.
            this.CallbackResults.CopyFrom(tskRC.CallbackResults);
            //this.CallbackResults.CopyFrom(taskResultsContainer.CallbackResults);
        }

        #endregion 

        #region Private Methods

        // Executes a query as specified by the passed-in parameters.  Note that queryExpression 
        // and field name are nullable
        private System.Data.DataTable ExecuteQuery(string queryExpression, string fieldName, 
            string layerID, string mapResourceName)
        {
            // Get the map resource having the passed-in name
            ESRI.ArcGIS.ADF.Web.DataSources.IMapResource commonMapResource =
                this.MapControl.MapResourceManagerInstance.ResourceItems.Find(mapResourceName).Resource
                as ESRI.ArcGIS.ADF.Web.DataSources.IMapResource;

            // Create a query functionality from the map resource
            ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality commonQueryFunctionality =
                (ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)commonMapResource.CreateFunctionality(
                typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), "query");

            // Create a query filter.  If the field name is specified, use it as the filter's sub-field. If 
            // the query expression is specified, use it as the filter's where clause.
            ESRI.ArcGIS.ADF.Web.QueryFilter adfQueryFilter = new ESRI.ArcGIS.ADF.Web.QueryFilter();
            adfQueryFilter.ReturnADFGeometries = false;
            if (fieldName != null)
                adfQueryFilter.SubFields = new ESRI.ArcGIS.ADF.StringCollection(fieldName, ',');
            if (queryExpression != null)
                adfQueryFilter.WhereClause = queryExpression;

            // Get the map resource's map functionality
            ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality commonMapFunctionality =
                this.MapControl.GetFunctionality(commonMapResource);

            // Execute the query, returning the result to the method caller
            return commonQueryFunctionality.Query(commonMapFunctionality.Name, layerID, adfQueryFilter);
        }

        private System.Data.DataTable ExecuteQuery1(string queryExpression, string fieldName,
           string layerID, string mapResourceName)
        {
            // Get the map resource having the passed-in name
            ESRI.ArcGIS.ADF.Web.DataSources.IMapResource commonMapResource =
                this.MapControl.MapResourceManagerInstance.ResourceItems.Find(mapResourceName).Resource
                as ESRI.ArcGIS.ADF.Web.DataSources.IMapResource;

            //Map map = Page.FindControl("Map1") as Map;

            // Create a query functionality from the map resource
            ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality commonQueryFunctionality =
                (ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)commonMapResource.CreateFunctionality(
                typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), "query");

            // Create a query filter.  If the field name is specified, use it as the filter's sub-field. If 
            // the query expression is specified, use it as the filter's where clause.
            ESRI.ArcGIS.ADF.Web.QueryFilter adfQueryFilter = new ESRI.ArcGIS.ADF.Web.QueryFilter();
            adfQueryFilter.ReturnADFGeometries = false;
            if (fieldName != null)
                adfQueryFilter.SubFields = new ESRI.ArcGIS.ADF.StringCollection(fieldName, ',');
            if (queryExpression != null)
                adfQueryFilter.WhereClause = queryExpression;

            // Get the map resource's map functionality
            ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality commonMapFunctionality =
                this.MapControl.GetFunctionality(commonMapResource);
            //
            ESRI.ArcGIS.ADF.Web.UI.WebControls.Map map = Page.FindControl("Map1") as Map;
            ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat layerFormat = null;
            System.Data.DataTable identifyTable = commonQueryFunctionality.Query(commonMapFunctionality.Name, layerID, adfQueryFilter);
            System.Data.DataTable formattedTable = identifyTable;

            ESRI.ArcGIS.ADF.Web.DataSources.IGISResource resource;
            ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality query;
            System.Data.DataTable[] identifyDataTables = null;
            System.Data.DataTable identifyDataTable1 = null;

            //find
            ESRI.ArcGIS.ADF.Web.DataSources.IGISResource gisResource = commonMapResource;
            bool supported = gisResource.SupportsFunctionality(typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality));

            if (supported)
            {
                ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality queryFunctionality =
                (ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)gisResource.CreateFunctionality
                (typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), null);

                string[] layerIDs = null;
                string[] layerNames = null;
                queryFunctionality.GetQueryableLayers(null, out layerIDs, out layerNames);

                System.Collections.Generic.Dictionary<object, string[]>
                fieldsDictionary =
                new System.Collections.Generic.Dictionary<object, string[]>();

                foreach (string layerID1 in layerIDs)
                {
                    string[] fieldsArray = queryFunctionality.GetFields(null, layerID1);
                    fieldsDictionary.Add(layerID1, fieldsArray);
                }

                ESRI.ArcGIS.ADF.Web.FindParameters findParameters =
                    new ESRI.ArcGIS.ADF.Web.FindParameters();
                findParameters.FindOption = ESRI.ArcGIS.ADF.Web.FindOption.AllLayers;
                findParameters.LayersAndFields = fieldsDictionary;
                findParameters.FindString = fieldName;
                findParameters.MaxRecords = 10;
                findParameters.ReturnADFGeometries = true;
                findParameters.UseSqlContains = true;

                System.Data.DataTable[] queryResultsDataTableArray =
                    queryFunctionality.Find(null, findParameters);
            }
            //find


            foreach (ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality mapFunc in map.GetFunctionalities())
            {
                if (mapFunc.DisplaySettings.Visible)
                {
                    resource = mapFunc.Resource;
                    query = resource.CreateFunctionality(typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), "query_") as ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality;

                    if ((query != null) && (query.Supports("query")))
                    {
                        // #region Perform the identify query
                        // create a list of layer ids for the identify request
                        string[] layerNames = null;
                        string[] layerIDs = null;
                        query.GetQueryableLayers(null, out layerIDs, out layerNames);
                        try
                        {
                            identifyDataTable1 = query.Query(mapFunc.Name, layerIDs[7], adfQueryFilter);//mapPoint
                        }
                        catch
                        {
                            identifyDataTables = null;
                        }
                    }
                }
            }


            if (!string.IsNullOrEmpty(layerID))
            {
                layerFormat = ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat.FromMapResourceManager(map.MapResourceManagerInstance, commonMapFunctionality.Resource.Name, layerID);
                if (layerFormat != null)
                {
                    ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer layer = ESRI.ArcGIS.ADF.Web.Converter.ToGraphicsLayer(
                        identifyTable, System.Drawing.Color.Empty, System.Drawing.Color.Aqua, System.Drawing.Color.Red, true);
                    if (layer != null)
                    {
                        layerFormat.Apply(layer);
                        formattedTable = layer;
                    }
                }
            }
            //
            // Execute the query, returning the result to the method caller
            return commonQueryFunctionality.Query(commonMapFunctionality.Name, layerID, adfQueryFilter);
        }
        #region UI Creation Methods

        // Creates an HTML table containing a label, drop-down list, and div containing activity
        // indicators
        private System.Web.UI.WebControls.Table CreateDropDownListSection(
            ref System.Web.UI.HtmlControls.HtmlGenericControl dropDownList,
            ref System.Web.UI.HtmlControls.HtmlGenericControl activityDiv, string dropDownLabelText,
            string activityText)
        {
            // Use the method overload to create the table and add the label and drop-down list
            System.Web.UI.WebControls.Table table =
                this.CreateDropDownListSection(ref dropDownList, dropDownLabelText);

            // Call the method that will create a div with an activity indicator and label
            activityDiv = this.CreateActivityDiv(activityText);

            // Get the table cell containing the drop-down list and add the activity div to it
            System.Web.UI.WebControls.TableCell tableCell = table.Rows[0].Cells[1];
            tableCell.Controls.Add(activityDiv);

            return table;
        }

        // Creates an HTML table containing a label and drop-down list
        private System.Web.UI.WebControls.Table CreateDropDownListSection(
            ref System.Web.UI.HtmlControls.HtmlGenericControl dropDownList, string dropDownLabelText)
        {
            // Instantiate an HTML table and add it to the task's controls collection
            System.Web.UI.WebControls.Table table = new System.Web.UI.WebControls.Table();
            table.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Center;
            this.Controls.Add(table);

            // Create a table row to hold the controls
            System.Web.UI.WebControls.TableRow tableRow = new System.Web.UI.WebControls.TableRow();
            table.Rows.Add(tableRow);

            // Create a table cell to hold the label
            System.Web.UI.WebControls.TableCell tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Create a label, assign it the passed-in text, and add it to the table cell
            System.Web.UI.WebControls.Label label = new System.Web.UI.WebControls.Label();
            label.Text = dropDownLabelText;
            tableCell.Controls.Add(label);

            // Create a table cell to hold the drop-down list
            tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the drop-down list and add it to the table cell
            dropDownList = new System.Web.UI.HtmlControls.HtmlGenericControl("select");
            tableCell.Controls.Add(dropDownList);

            return table;
        }

        // Returns a div containing an activity indictor with the passed-in text
        private System.Web.UI.HtmlControls.HtmlGenericControl CreateActivityDiv(string activityText)
        {
            // Initialize a div to hold the activity indicator.  Specify that the div be hidden and that
            // its contents are always on the same line.
            System.Web.UI.HtmlControls.HtmlGenericControl containerDiv =
                new System.Web.UI.HtmlControls.HtmlGenericControl("div");
            containerDiv.Style[System.Web.UI.HtmlTextWriterStyle.Display] = "none";
            containerDiv.Style[System.Web.UI.HtmlTextWriterStyle.WhiteSpace] = "nowrap";

            // Initialize an image with an activity indicator and add it to the div
            System.Web.UI.WebControls.Image activityImage = new System.Web.UI.WebControls.Image();
            activityImage.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(
                typeof(SimpleScriptTask), "ESRI.ADF.UserSearch.CustomTasks.images.activity_indicator.gif");
            containerDiv.Controls.Add(activityImage);

            // Initialize a label to display along with the activity indicator image and add it to the div
            System.Web.UI.WebControls.Label activityLabel = new System.Web.UI.WebControls.Label();
            activityLabel.Text = activityText;
            containerDiv.Controls.Add(activityLabel);

            return containerDiv;
        }

        // Creates an HTML table containing the Add to Query button and adds it to the task's 
        // controls collection
        private void CreateAddToQueryButtonSection()
        {
            // Create an HTML table to hold the Add to Query button
            System.Web.UI.WebControls.Table addToQueryButtonTable = new System.Web.UI.WebControls.Table();
            addToQueryButtonTable.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Center;
            this.Controls.Add(addToQueryButtonTable);

            // Create a row and cell to hold the button
            System.Web.UI.WebControls.TableRow tableRow = new System.Web.UI.WebControls.TableRow();
            addToQueryButtonTable.Rows.Add(tableRow);

            System.Web.UI.WebControls.TableCell tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the button, specify its text, and add it to the table cell
            _addToQueryButton = new System.Web.UI.HtmlControls.HtmlInputButton();
            _addToQueryButton.Value = "Thêm lệnh truy vấn";
            tableCell.Controls.Add(_addToQueryButton);
        }

        // Creates an HTML table containing a horizontal rule and adds it to the task's controls
        // collection
        private void CreateSeparator()
        {
            // Create a table to hold the horizontal rule, specifying a width of 80%
            System.Web.UI.WebControls.Table separatorTable = new System.Web.UI.WebControls.Table();
            separatorTable.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Center;
            System.Web.UI.WebControls.Unit unit = new System.Web.UI.WebControls.Unit(80, System.Web.UI.WebControls.UnitType.Percentage);
            separatorTable.Width = unit;
            this.Controls.Add(separatorTable);

            // Create a row and cell to contain the rule
            System.Web.UI.WebControls.TableRow tableRow = new System.Web.UI.WebControls.TableRow();
            separatorTable.Rows.Add(tableRow);

            System.Web.UI.WebControls.TableCell tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate a horizontal rule and add it to the cell
            System.Web.UI.HtmlControls.HtmlGenericControl horizontalRule =
                new System.Web.UI.HtmlControls.HtmlGenericControl("hr");
            tableCell.Controls.Add(horizontalRule);
        }

        // Creates an HTML table containing the operator buttons and adds it to the task's 
        // controls collection
        private void CreateOperatorButtonsSection()
        {
            // Instantiate a table to hold the first row of operator buttons
            System.Web.UI.WebControls.Table operatorButtonTable = new System.Web.UI.WebControls.Table();
            operatorButtonTable.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Center;
            this.Controls.Add(operatorButtonTable);

            System.Web.UI.WebControls.TableRow tableRow = new System.Web.UI.WebControls.TableRow();
            operatorButtonTable.Rows.Add(tableRow);

            // Create a cell to hold the left parenthesis button
            System.Web.UI.WebControls.TableCell tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the left parenthesis button, assign its text, and add it to the cell
            _leftParenthesisButton = new System.Web.UI.HtmlControls.HtmlInputButton();
            _leftParenthesisButton.Value = "(";
            tableCell.Controls.Add(_leftParenthesisButton);

            // Create a cell to hold the right parenthesis button
            tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the right parenthesis button, assign its text, and add it to the cell
            _rightParenthesisButton = new System.Web.UI.HtmlControls.HtmlInputButton();
            _rightParenthesisButton.Value = ")";
            tableCell.Controls.Add(_rightParenthesisButton);

            // Create a cell to hold the percent button
            tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the percent button, assign its text, and add it to the cell
            _percentButton = new System.Web.UI.HtmlControls.HtmlInputButton();
            _percentButton.Value = "%";
            tableCell.Controls.Add(_percentButton);

            // Create a cell to thold the underscore button
            tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the underscore button, assign its text, and add it to the cell
            _underscoreButton = new System.Web.UI.HtmlControls.HtmlInputButton();
            _underscoreButton.Value = "_";
            tableCell.Controls.Add(_underscoreButton);

            // Create a new table to hold the And, Or, and Not buttons
            operatorButtonTable = new System.Web.UI.WebControls.Table();
            operatorButtonTable.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Center;
            this.Controls.Add(operatorButtonTable);

            tableRow = new System.Web.UI.WebControls.TableRow();
            operatorButtonTable.Rows.Add(tableRow);

            // Create a cell to hold the And button
            tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the And button, assign its text, and add it to the cell
            _andButton = new System.Web.UI.HtmlControls.HtmlInputButton();
            _andButton.Value = "And";
            tableCell.Controls.Add(_andButton);

            // Create a cell to hold the Or button
            tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the Or button, assign its text, and add it to the cell
            _orButton = new System.Web.UI.HtmlControls.HtmlInputButton();
            _orButton.Value = "Or";
            tableCell.Controls.Add(_orButton);

            // Create a cell to hold the Not button
            tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the Not button, assign its text, and add it to the cell
            _notButton = new System.Web.UI.HtmlControls.HtmlInputButton();
            _notButton.Value = "Not";
            tableCell.Controls.Add(_notButton);
        }

        // Creates an HTML table containing the query textbox and label and adds it to the 
        // task's controls collection
        private void CreateQuerySection()
        {
            // Creaet a table to hold the query label and textbox, specifying a width of 95%
            System.Web.UI.WebControls.Table queryTable = new System.Web.UI.WebControls.Table();
            queryTable.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Center;
            System.Web.UI.WebControls.Unit unit = new System.Web.UI.WebControls.Unit(
                95, System.Web.UI.WebControls.UnitType.Percentage);
            queryTable.Width = unit;
            this.Controls.Add(queryTable);

            // Create a row and cell to hold the query textbox label
            System.Web.UI.WebControls.TableRow tableRow = new System.Web.UI.WebControls.TableRow();
            queryTable.Rows.Add(tableRow);            
            System.Web.UI.WebControls.TableCell tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the query textbox label, assign its text, and add it to the cell
            System.Web.UI.WebControls.Label queryLabel = new System.Web.UI.WebControls.Label();
            queryLabel.Text = "Câu truy vấn:";
            tableCell.Controls.Add(queryLabel);

            // Create a row and cell to hold the query textbox
            tableRow = new System.Web.UI.WebControls.TableRow();
            queryTable.Rows.Add(tableRow);
            tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the query textbox.  Specify multi-line capability, word wrapping, and a 
            // height of 80 pixels.  Then add it to its container cell.
            _queryTextBox = new System.Web.UI.WebControls.TextBox();
            _queryTextBox.TextMode = System.Web.UI.WebControls.TextBoxMode.MultiLine;
            _queryTextBox.Wrap = true;
            _queryTextBox.Height = (System.Web.UI.WebControls.Unit)80;
            _queryTextBox.Width = unit;

            tableCell.Controls.Add(_queryTextBox);
        }

        // Creates an HTML table containing the Clear Query and Execute Query buttons and adds
        // it to the task's controls collection
        private void CreateQueryButtonsSection()
        {
            // Create a table to hold the Clear Query and Execute Query buttons
            System.Web.UI.WebControls.Table queryButtonTable = new System.Web.UI.WebControls.Table();
            queryButtonTable.HorizontalAlign = System.Web.UI.WebControls.HorizontalAlign.Center;
            this.Controls.Add(queryButtonTable);

            System.Web.UI.WebControls.TableRow tableRow = new System.Web.UI.WebControls.TableRow();
            queryButtonTable.Rows.Add(tableRow);

            // Create a table cell to hold the Clear Query button
            System.Web.UI.WebControls.TableCell tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the Clear Query button, specify its text, and add it to its container cell
            _clearQueryButton = new System.Web.UI.HtmlControls.HtmlInputButton();
            _clearQueryButton.Value = "Xóa lệnh truy vấn";
            tableCell.Controls.Add(_clearQueryButton);

            // Create a cell to hold the Execute Query button
            tableCell = new System.Web.UI.WebControls.TableCell();
            tableRow.Cells.Add(tableCell);

            // Instantiate the Execute Query button, specify its text, and add it to its container cell
            _doQueryButton = new System.Web.UI.HtmlControls.HtmlInputButton();
            _doQueryButton.Value = "Thực hiện truy vấn";
            tableCell.Controls.Add(_doQueryButton);

            // Call method to create the query execution activity indicator div and add it to the same
            // cell as the Execute Query button
            _executingQueryDiv = this.CreateActivityDiv("Truy vấn đang thực thi...");
            tableCell.Controls.Add(_executingQueryDiv);
        }

        #endregion 

        #endregion

        #endregion

        #region Instance Properties

        #region Public Properties

        // Specifies that the property will appear in Visual Studio's properties pane
        [System.ComponentModel.Browsable(true)]
        // Specifies the category within which the property will be grouped
        [System.ComponentModel.Category("BuddyControls")]
        // Specifies the property's default value
        [System.ComponentModel.DefaultValue("Map1")]
        // Stores the ID of the map to which the task is buddied
        public string Map
        {
            get
            {
                // Attempt to retrieve the Map ID from state
                string mapID = StateManager.GetProperty("MapID") as string;
                // If no Map ID was stored in state, return the default value.  Otherwise,
                // return the stroed value.
                return (mapID == null) ? "Map1" : mapID;
            }
            set
            {
                // Store the passed-in value in state
                StateManager.SetProperty("MapID", value);
            }
        }

        // Specifies that the property will appear in Visual Studio's properties pane
        [System.ComponentModel.Browsable(true)]
        // Specifies the category within which the property will be grouped
        [System.ComponentModel.Category("Task")]
        // Specifies the property's default value
        [System.ComponentModel.DefaultValue("True")]
        // Indicates whether queryable layers belonging to graphics resources should be included for 
        // querying by the task
        public bool ExcludeGraphicsResources
        {
            get
            {
                // If the property has not been stored in state, return true.  Otherwise, return the stored
                // value.
                bool excludeGraphicsResources = true;
                if (StateManager.GetProperty("ExcludeGraphicsResources") != null)
                    excludeGraphicsResources = (bool)StateManager.GetProperty("ExcludeGraphicsResources");
                return excludeGraphicsResources;
            }
            set
            {
                // Store the passed-in value in state
                StateManager.SetProperty("ExcludeGraphicsResources", value);
            }
        }

        #endregion

        // Provides easy access to the ADF Map control referred to by the ID stored in the task's 
        // Map property
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.Map MapControl
        {
            get
            {
                // Retrieve the map control specified by the task's Map property.  Note that this
                // is retrieved dynamically whenever the property is called so that the correct
                // map is always returned (i.e. even after changing the Map property).
                ESRI.ArcGIS.ADF.Web.UI.WebControls.Map adfMap = 
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControl(
                    this.Map, this.Page) as ESRI.ArcGIS.ADF.Web.UI.WebControls.Map;
                return adfMap;
            }
        }

        #endregion
    }
}
